package sale

import (
	mc "bbtradeByGo/model/match"
	rmc "bbtradeByGo/model/match/model"
	"bbtradeByGo/pkg/email"
	"bbtradeByGo/pkg/gredis"
	"bbtradeByGo/pkg/logging"
	"bbtradeByGo/service/match/helper"
	"encoding/json"
	"fmt"
	"github.com/gomodule/redigo/redis"
	"math/big"
)


//Sale 卖单
func Sale(condition *rmc.MCondition) {
	/*
	 *  1--> `卖单数量` > `买单数量` ==> 继续撮合    更新卖单 移除队列中的买单
	 *  2--> `卖单数量` < `买单数量` ==> 停止撮合    更新队列中的买单
	 *  0--> `卖单数量` = `买单数量` ==> 停止撮合    移除队列中的买单
	 */
	saleKey     := fmt.Sprintf("currency:match:sale:%v/%v", condition.BaseCurrencyId, condition.CurrencyId)
	purChaseKey := fmt.Sprintf("currency:match:purchase:%v/%v", condition.BaseCurrencyId, condition.CurrencyId)
	cancleKey   := fmt.Sprintf("currency:match:cancle:%v/%v", condition.BaseCurrencyId, condition.CurrencyId)
    //做买的事情
	purChaseList, err := redis.Values(gredis.LRange(purChaseKey, 0, -1))
	if err != nil {
		email.SendMail("service.match.sale.Sale purChaseList", err.Error())
		logging.Error("service.match.sale.Sale purChaseList err: ", err)
	}
	if len(purChaseList) > 0 {
		for i, vs := range purChaseList{
			var v = rmc.MCondition{}
			err = json.Unmarshal(vs.([]byte), &v)
			if err != nil {
				email.SendMail("service.match.sale.Sale json.Unmarshal", err.Error())
				logging.Error("service.match.sale.Sale json.Unmarshal err:", err)
			}
			//市价交易
			if  condition.Tradetype == 1{
				condition.Price = v.Price
			}
			//1.卖方价小于买方价，进行撮合
			if(condition.Price <= v.Price){
                res := Match(condition, &v, purChaseKey, saleKey, cancleKey)
                if res == nil {return}

                switch res.IsContinue{
				case 1:
					condition = res.Sale
					//买单抵消， 删除买单， 卖单接着下一轮撮合
					_, err := gredis.LRem(purChaseKey, res.Purchase, 1)
					if err != nil {
						email.SendMail("service.match.sale.Sale gredis.LRem1", err.Error())
						logging.Error("service.match.sale.Sale gredis.LRem err1:", err)
					}
					//卖单价不够成交
					if i == len(purChaseList) - 1{
						//如果市价单要成交的数量大于目前限价单的数量，取消市价单
						if condition.Tradetype == 1{
                            mc.OrderCancel(condition)
						}else {
							//限价单放入到队列中
							Push(condition, saleKey)
						}
					}
				case 2:
					//不能直接删除，防止lrem数据后，序号改变。需要重新遍历一遍
					gredis.LRem(purChaseKey, v, 1)
					if err != nil {
						email.SendMail("service.match.sale.Sale gredis.LRem2", err.Error())
						logging.Error("service.match.sale.Sale gredis.LRem err2:", err)
					}
					gredis.LPush(purChaseKey, res.Purchase)
				case 3:
					//买单撮合失败（常出现在市价交易情况，余额不够进行市价交易），自动取消订单
					mc.OrderCancel(condition)
				case 0:
					gredis.LRem(purChaseKey, res.Purchase, 1)
					if err != nil {
						email.SendMail("service.match.sale.Sale gredis.LRem3", err.Error())
						logging.Error("service.match.sale.Sale gredis.LRem err3:", err)
					}
				}
                if res.IsContinue != 1{
					return
				}else {
					//2.把卖单放入卖单集合
					Push(condition, saleKey)
					return
				}
			}
		}
	}else {
		//没有足够的买单
		//如果是市价交易，取消订单
		if condition.Tradetype == 1 {
			mc.OrderCancel(condition)
		}else {
			Push(condition, saleKey)
		}
	}
}

//Match 撮合订单
func Match(sale, purchase *rmc.MCondition, purchaseKey, saleKey, cancleKey string) *rmc.MakeUp{
   //获取基础币的买入和卖出手续费
    fees, err  := mc.FeeGet(sale.CurrencyId, sale.BaseCurrencyId)
    if err != nil {
   	 email.SendMail("service.match.sale.Match", err.Error())
   	 logging.Error("service.match.sale.Match err:", err)
    }
    if fees == nil{return nil}
    sellFee := fees.SellFee
    buyFee  := fees.BuyFee

	//买单是否取消
    isOk := IsCancel(cancleKey, purchase)
    if isOk {
	   return &rmc.MakeUp{
	   	 IsContinue: 1,
	   	 Purchase: purchase,
	   	 Sale: sale,
	   }
    }
    var isContinue int
    //撮合卖出订单，按照用户买入价成交
    if sale.Surplus > purchase.Surplus {
   	   sale = SaleGt(purchase, sale, sellFee, buyFee)
   	   isContinue = 1
    }else if sale.Surplus < purchase.Surplus{
       purchase = PurChaseGt(purchase, sale, sellFee, buyFee)
       isContinue = 2
    }else {
       PsEq(purchase, sale, sellFee, buyFee)
       isContinue = 0
    }
	return &rmc.MakeUp{
		IsContinue: isContinue,
		Purchase: purchase,
		Sale: sale,
	}
}

//订单是否被取消
func IsCancel(cancelKey string, purchase *rmc.MCondition)  bool {
	cancelList, err := redis.Values(gredis.LRange(cancelKey, 0, -1))
	if err != nil {
		email.SendMail("service.match.sale.IsCancel cancelList", err.Error())
		logging.Error("service.match.sale.IsCancel cancelList err:", err)
	}

	if IndexOf(purchase.Id, cancelList) > -1 {
		//移除取消订单队列
		_, err := redis.Int(gredis.LRem(cancelKey, purchase.Id, 1))
		if err != nil {
			email.SendMail("service.match.sale.IsCancel redis.Int", err.Error())
			logging.Error("service.match.sale.IsCancel redis.Int err:", err)
		}
		return true
	}
	return false
}

//IndexOf 判断数组中是否存在某个元素
func IndexOf(element int, data []interface{}) (int) {
	for k, v := range data {
		if fmt.Sprintf("%d",element) == fmt.Sprintf("%s",v) {
			return k
		}
	}
	return -1    //not found.
}

//SaleGt 卖方数量出大于买方数量
func SaleGt(purchase, sale *rmc.MCondition, sellFee, buyFee float64) *rmc.MCondition{
	taker := 0
	price  := purchase.Price
	//可被撮合数量
	number := purchase.Surplus

	helper.DoMatch(purchase, sale, sellFee, buyFee, number, price, taker)

	saleTrademoney     := big.NewFloat(sale.Trademoney)
	saleSurplus        := big.NewFloat(sale.Surplus)
	num      := big.NewFloat(number)
	pri      := big.NewFloat(price)
	logging.Info("service.match.sale.SaleGt saleTrademoney:", saleTrademoney)
	logging.Info("service.match.sale.SaleGt saleSurplus:", saleSurplus)
	logging.Info("service.match.sale.SaleGt num:", num)
	logging.Info("service.match.sale.SaleGt pri:", pri)



	mulResult := num.Mul(num, pri)
	logging.Info("service.match.sale.SaleGt mulResult:", mulResult)

	sale.Trademoney, _ = mulResult.Add(mulResult, saleTrademoney).Float64()
	sale.Surplus, _    = saleSurplus.Sub(saleSurplus, big.NewFloat(number)).Float64()
	logging.Info("service.match.sale.SaleGt sale.Trademoney:", sale.Trademoney)
	logging.Info("service.match.sale.SaleGt sale.Surplus:", sale.Surplus)

	// 1->部分成交
	sale.OrderStatus = 1
	return sale
}

//PurChaseGt 买入大于卖出 剩余买入
func PurChaseGt(purchase, sale *rmc.MCondition, sellFee, buyFee float64) *rmc.MCondition{
	taker := 0
	price  := purchase.Price
	//可被撮合数量
	number := sale.Surplus

	helper.DoMatch(purchase, sale, sellFee, buyFee, number, price, taker)

	purchaseTrademoney     := big.NewFloat(purchase.Trademoney)
	purchaseSurplus        := big.NewFloat(purchase.Surplus)
	num      := big.NewFloat(number)
	pri      := big.NewFloat(price)

	logging.Info("service.match.sale.PurChaseGt purchaseTrademoney:", purchaseTrademoney)
	logging.Info("service.match.sale.PurChaseGt purchaseSurplus:", purchaseSurplus)
	logging.Info("service.match.sale.PurChaseGt num:", num)
	logging.Info("service.match.sale.PurChaseGt pri:", pri)

	mulResult := num.Mul(num, pri)
	logging.Info("service.match.sale.PurChaseGt mulResult:", mulResult)

	purchase.Trademoney, _ = mulResult.Add(purchaseTrademoney, mulResult).Float64()
	purchase.Surplus, _    = purchaseSurplus.Sub(purchaseSurplus, big.NewFloat(number)).Float64()
	logging.Info("service.match.sale.PurChaseGt purchase.Trademoney:", purchase.Trademoney)
	logging.Info("service.match.sale.PurChaseGt purchase.Surplus:", purchase.Surplus)

	// 1->部分成交
	purchase.OrderStatus = 1
	return purchase
}

//PsEq 卖出等于买入
func PsEq(purchase, sale *rmc.MCondition, sellFee, buyFee float64){
	taker := 0
	price  := purchase.Price
	//可被撮合数量
	number := sale.Surplus

	helper.DoMatch(purchase, sale, sellFee, buyFee, number, price, taker)
}

// Push 将卖单放入集合中
func Push(condition *rmc.MCondition, saleKey string) {
    saleList, err := redis.Values(gredis.LRange(saleKey, 0, -1))
    if err != nil {
		email.SendMail("service.match.sale.Push saleList", err.Error())
		logging.Error("service.match.sale.Push err:", err)
	}

    if len(saleList) > 0 {
    	for i, v := range saleList{
			s := rmc.MCondition{}
			err = json.Unmarshal(v.([]byte), &s)
			if err != nil {
				email.SendMail("service.match.sale.Push json.Unmarshal", err.Error())
				logging.Error("service.match.sale.Push err:", err)
			}

			if s.Price > condition.Price {
    			gredis.LInsert(saleKey, "before", s, condition)
				return
			}else if i == len(saleList) - 1 {
				gredis.RPush(saleKey, condition)
			}
		}
	}else {
		gredis.RPush(saleKey, condition)
	}
}
